{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Day11 正则中的“非获取匹配”"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　在开始今天的内容之前，我们先来看一个看起来似乎很简单的小例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-07T11:13:19.742025Z",
     "iopub.status.busy": "2020-09-07T11:13:19.742025Z",
     "iopub.status.idle": "2020-09-07T11:13:19.747011Z",
     "shell.execute_reply": "2020-09-07T11:13:19.746014Z",
     "shell.execute_reply.started": "2020-09-07T11:13:19.742025Z"
    }
   },
   "outputs": [],
   "source": [
    "s = 'expression specific standard textual representing patterns matching text distinct mathematical notation described'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　请你提取出所有长度在7到10之间的单词，如果你想当然的按照下面的做法来提取："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-07T11:13:19.749007Z",
     "iopub.status.busy": "2020-09-07T11:13:19.748011Z",
     "iopub.status.idle": "2020-09-07T11:13:19.764965Z",
     "shell.execute_reply": "2020-09-07T11:13:19.764965Z",
     "shell.execute_reply.started": "2020-09-07T11:13:19.748011Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['expression',\n",
       " 'specific',\n",
       " 'standard',\n",
       " 'textual',\n",
       " 'representi',\n",
       " 'patterns',\n",
       " 'matching',\n",
       " 'distinct',\n",
       " 'mathematic',\n",
       " 'notation',\n",
       " 'described']"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import re\n",
    "\n",
    "re.findall('[a-z]{6,10}', s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　你会发现，结果中很多都不是完整单词，即我们先前的正则会把某些长度超过上限10的单词，其前10个字符截断下来作为结果返回，这一点你不能责怪正则，因为它确实是老老实实按照你的要求来做的，那我们是不是可以按照之前日程里学到的代表非单词的`\\W`来辅助标识每个单词的两边呢？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-07T11:13:19.766999Z",
     "iopub.status.busy": "2020-09-07T11:13:19.766999Z",
     "iopub.status.idle": "2020-09-07T11:13:19.780924Z",
     "shell.execute_reply": "2020-09-07T11:13:19.779926Z",
     "shell.execute_reply.started": "2020-09-07T11:13:19.766999Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['expression', 'standard', 'patterns', 'distinct', 'notation']"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 为了方便对开头结尾的匹配，给原始字符串两边各连接上一个空格\n",
    "re.findall('\\W+([a-z]{7,10})\\W+', ' '+s+' ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　似乎这样写解决了我们的问题，但仔细观察会发现很多符合要求的词并没有被提取到，这是因为我们上面的正则中两边的`\\W+`在匹配单词边界的过程中会被消耗。\n",
    "  \n",
    "　　譬如`expression`有10个单词，且经过处理后两边都是空格满足正则的要求，但其右侧的空格在完成对`expression`的匹配之后，便被消耗掉了，导致正则继续往右尝试匹配`specific`时，由于`specific`左侧空格已经被消耗，所以错过了`specific`。\n",
    "  \n",
    "　　而我们今天要介绍的“非获取匹配”指的是在匹配过程中匹配的模式，在经过“非获取匹配”类的表达式匹配之后不会被消耗，譬如下面的`(?=模式)修饰符`的用法，称作“正向肯定预查”，即用来表示目标字符串之后的辅助模式，下面的正则可以翻译为**匹配两边是非字母的7到10字单词，且右侧辅助定位的非单词部分在匹配之后仍然可以接着被之后的匹配过程使用到**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-07T11:13:19.782917Z",
     "iopub.status.busy": "2020-09-07T11:13:19.781920Z",
     "iopub.status.idle": "2020-09-07T11:13:19.796881Z",
     "shell.execute_reply": "2020-09-07T11:13:19.795884Z",
     "shell.execute_reply.started": "2020-09-07T11:13:19.782917Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['expression',\n",
       " 'specific',\n",
       " 'standard',\n",
       " 'textual',\n",
       " 'patterns',\n",
       " 'matching',\n",
       " 'distinct',\n",
       " 'notation',\n",
       " 'described']"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "re.findall('\\W+([a-z]{7,10})(?=\\W)+', ' '+s+' ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-07T10:56:04.890488Z",
     "iopub.status.busy": "2020-09-07T10:56:04.889492Z",
     "iopub.status.idle": "2020-09-07T10:56:04.896473Z",
     "shell.execute_reply": "2020-09-07T10:56:04.895476Z",
     "shell.execute_reply.started": "2020-09-07T10:56:04.890488Z"
    }
   },
   "source": [
    "　　这时我们可以发现结果就恢复了正常，至此我们已经了解了什么是“非获取匹配”，明天的日程里我将带大家学习常用的几种“非获取匹配方法”。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Day11 课后小测验\n",
    "\n",
    "　　趁热打铁，请你从下面的字符串中提取出所有的手机号码（1开头的连续11位数字）及座机号码（若干数字+`-`+若干数字）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-07T11:13:19.797878Z",
     "iopub.status.busy": "2020-09-07T11:13:19.797878Z",
     "iopub.status.idle": "2020-09-07T11:13:19.809847Z",
     "shell.execute_reply": "2020-09-07T11:13:19.808849Z",
     "shell.execute_reply.started": "2020-09-07T11:13:19.797878Z"
    }
   },
   "outputs": [],
   "source": [
    "target = '13988887777 18988887777 010-3232112 023-8988988 18900990099 dsddsa 21321 32232321232 fsfdf34134 1413431 0554-9888889'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['13988887777',\n",
       " '18988887777',\n",
       " '010-3232112',\n",
       " '023-8988988',\n",
       " '18900990099',\n",
       " '0554-9888889']"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "re.findall('\\W+(1\\d{10}|\\d+?-\\d+?)(?=\\W)+',' ' + target + ' ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-08-26T12:55:19.898241Z",
     "iopub.status.busy": "2020-08-26T12:55:19.897245Z",
     "iopub.status.idle": "2020-08-26T12:55:19.904225Z",
     "shell.execute_reply": "2020-08-26T12:55:19.903227Z",
     "shell.execute_reply.started": "2020-08-26T12:55:19.898241Z"
    }
   },
   "source": [
    "　　请将你的答案截图发到本帖评论区~"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
